/***************************************************************************
 * This code is an original creation. It is intended to be used for a one  *
 * reader, one card situation within the Micro Framework. This is intended *
 * to be used with the GemCore Serial Lite Pro evaluation board or the     *
 * Teridian 73S1209F evaluation board.                                     *
 * LAST MODIFIED: 2010 April 23                                            *
 * Copyright (C) 2009 Gemalto                                              *
 ***************************************************************************/
/***************************************************************************
 *  SHA1.cs is part of the PC/SC Micro API for the .NET Micro Framework.   *
 *                                                                         *
 *  The PC/SC Micro API for the .NET Micro Framework is free software:     *
 *  you can redistribute it and/or modify it under the terms of the GNU    *
 *  General Public License as published by the Free Software Foundation,   *
 *  either version 2 of the License, or (at your option) any later version.*
 *                                                                         *
 *  The PC/SC Micro API for the .NET Micro Framework is distributed in     *
 *  the hope that it will be useful, but WITHOUT ANY WARRANTY; without even*
 *  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR    *
 *  PURPOSE.  See the GNU General Public License for more details.         *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with the PC/SC Micro API for the .NET Micro Framework. If not,   *
 *  see <http://www.gnu.org/licenses/>.                                    *
 ***************************************************************************/
using System;
using Microsoft.SPOT;
using PCSCMicro;

namespace FileSignature
{
    class SHA1
    {
        static uint h0, h1, h2, h3, h4;
        
        //Main hash function
        //Input:    byte array containing the data to be hashed
        //Output:   string containing hash value for data
        public static string Hash(byte[] data)
        {
            //Initialize h variables
            h0 = 0x67452301;
            h1 = 0xEFCDAB89;
            h2 = 0x98BADCFE;
            h3 = 0x10325476;
            h4 = 0xC3D2E1F0;

            //Chunk variables
            byte[] chunk = new byte[64];
            byte place = 0;

            //Sort data into chunks
            for (int i = 0; i < data.Length; i++)
            {
                chunk[place++] = data[i];
                //Evaluate each chunk once size of chunk is 512 bits
                if (place == 64)
                {
                    evaluate(chunk);
                    place = 0;
                }
            }

            //Add one '1' bit to the end of the input
            chunk[place++] = 0x80;
            //Add '0' bits until (no. of bits % 512) = 448
            while (place != 56)
            {
                chunk[place++] = 0;
                //If ever have full chunk, evaluate it
                if (place == 64)
                {
                    evaluate(chunk);
                    place = 0;
                }
            }

            //Add size of input to the end
            UInt64 dataSize = (uint)data.Length*8;
            chunk[56] = (byte)(dataSize >> 56);
            chunk[57] = (byte)((dataSize >> 48) & 0xFF);
            chunk[58] = (byte)((dataSize >> 40) & 0xFF);
            chunk[59] = (byte)((dataSize >> 32) & 0xFF);
            chunk[60] = (byte)((dataSize >> 24) & 0xFF);
            chunk[61] = (byte)((dataSize >> 16) & 0xFF);
            chunk[62] = (byte)((dataSize >> 8) & 0xFF);
            chunk[63] = (byte)(dataSize & 0xFF);
            
            //Evaluate final chunk
            evaluate(chunk);

            //Return hash value
            return final();

        }

        //Private functions
        #region Private Functions

        //Evaluation function for each chunk of data
        //Input:    byte array containing chunk to be evaluated
        //Output:   --
        //Function  will update values of h0-h4 when completed
        static void evaluate(byte[] chunk)
        {
            //initialize variables
            uint f;
            uint k;
            uint a = h0;
            uint b = h1;
            uint c = h2; 
            uint d = h3;
            uint e = h4;

            //initialize array
            uint[] w = new uint[80];
            for (int i = 0; i < 80; i++)
            {
                if (i < 16)
                {
                    w[i] = bytes2Uint(chunk[i * 4], chunk[i * 4 + 1], chunk[i * 4 + 2], chunk[i * 4 + 3]);
                }
                else
                {
                    w[i] = leftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
                }
            }

            //perform hashing
            for (int i = 0; i < 80; i ++)
            {
                if (i < 20)
                {
                    f = (b & c) | ((~ b) & d);
                    k = 0x5A827999;
                }
                else if (i <40)
                {
                    f = b ^ c ^ d;
                    k = 0x6ED9EBA1;
                }
                else if (i < 60)
                {
                    f = (b & c) | (b & d) | (c & d);
                    k = 0x8F1BBCDC;
                }
                else
                {
                    f = b ^ c ^ d;
                    k = 0xCA62C1D6;
                }
                uint temp = leftRotate(a,5) + f + e + k + w[i];
                e = d;
                d = c;
                c = leftRotate(b, 30);
                b = a;
                a = temp;
            }
            h0 += a;
            h1 += b;
            h2 += c;
            h3 += d;
            h4 += e;
        }

        //Left rotate function
        //Input:    uint containing value to be rotated
        //          int containing number of rotations
        //Output:   uint containing rotated value
        static uint leftRotate(uint x, int n)
        {
            return ((x << n) | (x >> (32 - n)));
        }

        //Function to turn h0-h4 values into hash value string
        //Input:    --
        //Output:   string containing hash value for return
        static string final()
        {
            string hashValue = "";
            hashValue += HexStringDecoder.Dec2Hex(h0);
            hashValue += HexStringDecoder.Dec2Hex(h1);
            hashValue += HexStringDecoder.Dec2Hex(h2);
            hashValue += HexStringDecoder.Dec2Hex(h3);
            hashValue += HexStringDecoder.Dec2Hex(h4);
            return hashValue;
        }

        //Concatenate 4 bytes into uint
        //Input: 4 bytes containing data to be concatenated
        //Output: uint containing concatenated value 
        static uint bytes2Uint(byte a, byte b, byte c, byte d)
        {
            return (uint)(a << 24) | (uint)(b << 16) | (uint)(c << 8) | (uint)d;
        }
        #endregion
    }
}
